//------------------------------------------------------------------------------
// Original code by Christian Graus, modified by Ste Brennan for DNAide. 
// http://www.codeproject.com/cs/media/edge_detection.asp
//------------------------------------------------------------------------------
using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace DNAide.Drawing
{
	public static partial class BitmapAide
	{
		
		/// <summary>
		/// </summary>
		/// <param name="bitmap"></param>
		/// <returns></returns>
		public static void EdgeDetectQuick(Bitmap bitmap)
		{
			ConvMatrix m = new ConvMatrix();
			m.TopLeft = m.TopMid = m.TopRight = -1;
			m.MidLeft = m.Pixel = m.MidRight = 0;
			m.BottomLeft = m.BottomMid = m.BottomRight = 1;

			m.Offset = 127;

			Conv3x3(bitmap, m);
		}

		/// <summary>
		/// </summary>
		/// <param name="bitmap"></param>
		/// <param name="edgeDetectType"></param>
		/// <param name="threshold"></param>
		/// <returns></returns>
		public static void EdgeDetectConvolution(Bitmap bitmap, EdgeDetectType edgeDetectType, Byte threshold)
		{
			ConvMatrix m = new ConvMatrix();

			// I need to make a copy of this bitmap BEFORE I alter it 80)
			Bitmap bTemp = (Bitmap)bitmap.Clone();

			switch (edgeDetectType)
			{
				case EdgeDetectType.Sobel:
					m.SetAll(0);
					m.TopLeft = m.BottomLeft = 1;
					m.TopRight = m.BottomRight = -1;
					m.MidLeft = 2;
					m.MidRight = -2;
					m.Offset = 0;
					break;
				case EdgeDetectType.Prewitt:
					m.SetAll(0);
					m.TopLeft = m.MidLeft = m.BottomLeft = -1;
					m.TopRight = m.MidRight = m.BottomRight = 1;
					m.Offset = 0;
					break;
				case EdgeDetectType.Kirsh:
					m.SetAll(-3);
					m.Pixel = 0;
					m.TopLeft = m.MidLeft = m.BottomLeft = 5;
					m.Offset = 0;
					break;
			}

			Conv3x3(bitmap, m);

			switch (edgeDetectType)
			{
				case EdgeDetectType.Sobel:
					m.SetAll(0);
					m.TopLeft = m.TopRight = 1;
					m.BottomLeft = m.BottomRight = -1;
					m.TopMid = 2;
					m.BottomMid = -2;
					m.Offset = 0;
					break;
				case EdgeDetectType.Prewitt:
					m.SetAll(0);
					m.BottomLeft = m.BottomMid = m.BottomRight = -1;
					m.TopLeft = m.TopMid = m.TopRight = 1;
					m.Offset = 0;
					break;
				case EdgeDetectType.Kirsh:
					m.SetAll(-3);
					m.Pixel = 0;
					m.BottomLeft = m.BottomMid = m.BottomRight = 5;
					m.Offset = 0;
					break;
			}

			Conv3x3(bTemp, m);

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
			BitmapData bmData2 = bTemp.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;
			System.IntPtr Scan02 = bmData2.Scan0;

			unsafe
			{
				byte* p = (byte*)(void*)Scan0;
				byte* p2 = (byte*)(void*)Scan02;

				int nOffset = stride - bitmap.Width * 3;
				int nWidth = bitmap.Width * 3;

				int nPixel = 0;

				for (int y = 0; y < bitmap.Height; ++y)
				{
					for (int x = 0; x < nWidth; ++x)
					{
						nPixel = (int)Math.Sqrt((p[0] * p[0]) + (p2[0] * p2[0]));
						if (nPixel < threshold) nPixel = threshold;
						if (nPixel > 255) nPixel = 255;
						p[0] = (byte)nPixel;
						++p;
						++p2;
					}
					p += nOffset;
					p2 += nOffset;
				}
			}

			bitmap.UnlockBits(bmData);
			bTemp.UnlockBits(bmData2);
		}

		/// <summary>
		/// </summary>
		/// <param name="bitmap"></param>
		/// <returns></returns>
		public static void EdgeDetectHorizontal(Bitmap bitmap)
		{
			Bitmap bmTemp = (Bitmap)bitmap.Clone();

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
			BitmapData bmData2 = bmTemp.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;
			System.IntPtr Scan02 = bmData2.Scan0;

			unsafe
			{
				byte* p = (byte*)(void*)Scan0;
				byte* p2 = (byte*)(void*)Scan02;

				int nOffset = stride - bitmap.Width * 3;
				int nWidth = bitmap.Width * 3;

				int nPixel = 0;

				p += stride;
				p2 += stride;

				for (int y = 1; y < bitmap.Height - 1; ++y)
				{
					p += 9;
					p2 += 9;

					for (int x = 9; x < nWidth - 9; ++x)
					{
						nPixel = ((p2 + stride - 9)[0] +
							(p2 + stride - 6)[0] +
							(p2 + stride - 3)[0] +
							(p2 + stride)[0] +
							(p2 + stride + 3)[0] +
							(p2 + stride + 6)[0] +
							(p2 + stride + 9)[0] -
							(p2 - stride - 9)[0] -
							(p2 - stride - 6)[0] -
							(p2 - stride - 3)[0] -
							(p2 - stride)[0] -
							(p2 - stride + 3)[0] -
							(p2 - stride + 6)[0] -
							(p2 - stride + 9)[0]);

						if (nPixel < 0) nPixel = 0;
						if (nPixel > 255) nPixel = 255;

						(p + stride)[0] = (byte)nPixel;

						++p;
						++p2;
					}

					p += 9 + nOffset;
					p2 += 9 + nOffset;
				}
			}

			bitmap.UnlockBits(bmData);
			bmTemp.UnlockBits(bmData2);
		}


		/// <summary>
		/// </summary>
		/// <param name="bitmap"></param>
		public static void EdgeDetectVertical(Bitmap bitmap)
		{
			Bitmap bmTemp = (Bitmap)bitmap.Clone();

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
			BitmapData bmData2 = bmTemp.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;
			System.IntPtr Scan02 = bmData2.Scan0;

			unsafe
			{
				byte* p = (byte*)(void*)Scan0;
				byte* p2 = (byte*)(void*)Scan02;

				int nOffset = stride - bitmap.Width * 3;
				int nWidth = bitmap.Width * 3;

				int nPixel = 0;

				int nStride2 = stride * 2;
				int nStride3 = stride * 3;

				p += nStride3;
				p2 += nStride3;

				for (int y = 3; y < bitmap.Height - 3; ++y)
				{
					p += 3;
					p2 += 3;

					for (int x = 3; x < nWidth - 3; ++x)
					{
						nPixel = ((p2 + nStride3 + 3)[0] +
							(p2 + nStride2 + 3)[0] +
							(p2 + stride + 3)[0] +
							(p2 + 3)[0] +
							(p2 - stride + 3)[0] +
							(p2 - nStride2 + 3)[0] +
							(p2 - nStride3 + 3)[0] -
							(p2 + nStride3 - 3)[0] -
							(p2 + nStride2 - 3)[0] -
							(p2 + stride - 3)[0] -
							(p2 - 3)[0] -
							(p2 - stride - 3)[0] -
							(p2 - nStride2 - 3)[0] -
							(p2 - nStride3 - 3)[0]);

						if (nPixel < 0) nPixel = 0;
						if (nPixel > 255) nPixel = 255;

						p[0] = (byte)nPixel;

						++p;
						++p2;
					}

					p += 3 + nOffset;
					p2 += 3 + nOffset;
				}
			}

			bitmap.UnlockBits(bmData);
			bmTemp.UnlockBits(bmData2);
		}


		/// <summary>
		/// </summary>
		/// <param name="bitmap"></param>
		/// <param name="threshold"></param>
		public static void EdgeDetectHomogenity(Bitmap bitmap, Byte threshold)
		{
			// This one works by working out the greatest difference between a pixel and it's eight neighbours.
			// The threshold allows softer edges to be forced down to black, use 0 to negate it's effect.
			Bitmap b2 = (Bitmap)bitmap.Clone();

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
			BitmapData bmData2 = b2.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;
			System.IntPtr Scan02 = bmData2.Scan0;

			unsafe
			{
				byte* p = (byte*)(void*)Scan0;
				byte* p2 = (byte*)(void*)Scan02;

				int nOffset = stride - bitmap.Width * 3;
				int nWidth = bitmap.Width * 3;

				int nPixel = 0, nPixelMax = 0;

				p += stride;
				p2 += stride;

				for (int y = 1; y < bitmap.Height - 1; ++y)
				{
					p += 3;
					p2 += 3;

					for (int x = 3; x < nWidth - 3; ++x)
					{
						nPixelMax = Math.Abs(p2[0] - (p2 + stride - 3)[0]);
						nPixel = Math.Abs(p2[0] - (p2 + stride)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs(p2[0] - (p2 + stride + 3)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs(p2[0] - (p2 - stride)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs(p2[0] - (p2 + stride)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs(p2[0] - (p2 - stride - 3)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs(p2[0] - (p2 - stride)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs(p2[0] - (p2 - stride + 3)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						if (nPixelMax < threshold) nPixelMax = 0;

						p[0] = (byte)nPixelMax;

						++p;
						++p2;
					}

					p += 3 + nOffset;
					p2 += 3 + nOffset;
				}
			}

			bitmap.UnlockBits(bmData);
			b2.UnlockBits(bmData2);
		}


		/// <summary>
		/// </summary>
		/// <param name="bitmap"></param>
		/// <param name="threshold"></param>
		public static void EdgeDetectDifference(Bitmap bitmap, Byte threshold)
		{
			// This one works by working out the greatest difference between a pixel and it's eight neighbours.
			// The threshold allows softer edges to be forced down to black, use 0 to negate it's effect.
			Bitmap b2 = (Bitmap)bitmap.Clone();

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
			BitmapData bmData2 = b2.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;
			System.IntPtr Scan02 = bmData2.Scan0;

			unsafe
			{
				byte* p = (byte*)(void*)Scan0;
				byte* p2 = (byte*)(void*)Scan02;

				int nOffset = stride - bitmap.Width * 3;
				int nWidth = bitmap.Width * 3;

				int nPixel = 0, nPixelMax = 0;

				p += stride;
				p2 += stride;

				for (int y = 1; y < bitmap.Height - 1; ++y)
				{
					p += 3;
					p2 += 3;

					for (int x = 3; x < nWidth - 3; ++x)
					{
						nPixelMax = Math.Abs((p2 - stride + 3)[0] - (p2 + stride - 3)[0]);
						nPixel = Math.Abs((p2 + stride + 3)[0] - (p2 - stride - 3)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs((p2 - stride)[0] - (p2 + stride)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs((p2 + 3)[0] - (p2 - 3)[0]);
						if (nPixel > nPixelMax) nPixelMax = nPixel;

						if (nPixelMax < threshold) nPixelMax = 0;

						p[0] = (byte)nPixelMax;

						++p;
						++p2;
					}

					p += 3 + nOffset;
					p2 += 3 + nOffset;
				}
			}

			bitmap.UnlockBits(bmData);
			b2.UnlockBits(bmData2);
		}


		/// <summary>
		/// </summary>
		/// <param name="bitmap"></param>
		/// <param name="threshold"></param>
		/// <returns></returns>
		public static void EdgeEnhance(Bitmap bitmap, Byte threshold)
		{
			// This one works by working out the greatest difference between a nPixel and it's eight neighbours.
			// The threshold allows softer edges to be forced down to black, use 0 to negate it's effect.
			Bitmap b2 = (Bitmap)bitmap.Clone();

			// GDI+ still lies to us - the return format is BGR, NOT RGB.
			BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
			BitmapData bmData2 = b2.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;
			System.IntPtr Scan02 = bmData2.Scan0;

			unsafe
			{
				byte* p = (byte*)(void*)Scan0;
				byte* p2 = (byte*)(void*)Scan02;

				int nOffset = stride - bitmap.Width * 3;
				int nWidth = bitmap.Width * 3;

				int nPixel = 0, nPixelMax = 0;

				p += stride;
				p2 += stride;

				for (int y = 1; y < bitmap.Height - 1; ++y)
				{
					p += 3;
					p2 += 3;

					for (int x = 3; x < nWidth - 3; ++x)
					{
						nPixelMax = Math.Abs((p2 - stride + 3)[0] - (p2 + stride - 3)[0]);

						nPixel = Math.Abs((p2 + stride + 3)[0] - (p2 - stride - 3)[0]);

						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs((p2 - stride)[0] - (p2 + stride)[0]);

						if (nPixel > nPixelMax) nPixelMax = nPixel;

						nPixel = Math.Abs((p2 + 3)[0] - (p2 - 3)[0]);

						if (nPixel > nPixelMax) nPixelMax = nPixel;

						if (nPixelMax > threshold && nPixelMax > p[0])
							p[0] = (byte)Math.Max(p[0], nPixelMax);

						++p;
						++p2;
					}

					p += nOffset + 3;
					p2 += nOffset + 3;
				}
			}

			bitmap.UnlockBits(bmData);
			b2.UnlockBits(bmData2);
		}

	}

}
